/**
 *
 *  求曲线长度公式(参数):
 *  Integrals((dx/dt)^2 + (dy/dt)^2)^0.5 dt
 *
 *  非参数:
 *  Integrals((1 + (dy/dx)^2)^0.5) dt
 *
 *  f'(t) = (A*t*t + B * t + C)^0.5
 *
 */
class MyBezier{

    constructor(p0,p1,p2){
        let ax = p0.x - 2 * p1.x + p2.x;
        let ay = p0.y - 2 * p1.y + p2.y;
        let bx = 2 * p1.x - 2 * p0.x;
        let by = 2 * p1.y - 2 * p0.y;

        let A = 4 * (ax * ax + ay * ay);
        let B = 4 * (ax * bx + ay * by);
        let C = bx * bx + by * by;

        this.A = A;
        this.B = B;
        this.C = C;

        this.totalLength = this.ft(1);
        this.iStep = Math.floor(this.totalLength);
        this.p0 = p0;
        this.p1 = p1;
        this.p2 = p2;
    }

    /**
     * 求导
     * @param t
     * @return {number}
     */
    dft(t){
        let value = this.A * t * t + this.B * t + this.C;
        // value 可能 < 0
        if(value <= 0){
            console.log(`===========value:${value} 竟然 < 0`);
            return 0;
        }
        return Math.sqrt(value);
    }

    /**
     *  4 * a * c >= b * b
     * @param t [0,1]
     * @return {number}
     */
    ft(t){
        let A = this.A;
        let B = this.B;
        let C = this.C;

        let d = 4 * A * C - B * B;

        // bugfix: 注释掉 d === 0的情况就解决了. 为什么????
        // if(d === 0){
        //     console.log(`b^2 = 4 * a * c`);
        //     return t * t * Math.sqrt(A) / 2  + Math.sqrt(C) * t;
        // }

        /**
         * ((4*a*c-b^2)*asinh((2*a*x+b)/sqrt(4*a*c-b^2))+sqrt(a)*(4*a*x+2*b)*sqrt(a*x^2+b*x+c))/(8*a^(3/2))
         */
        // let sqrt_d = Math.sqrt(d);
        //
        // let k1 = (2 * A * t + B ) / sqrt_d;
        // let prePart = d * Math.asinh(k1);
        //
        // let sqrt_a = Math.sqrt(A);
        // let lastPart = sqrt_a * (4 * A * t + 2 * B) * Math.sqrt(A * t * t + B * t + C);
        //
        // let fm = 8 * Math.pow(A,1.5);
        //
        // let zeroPrePart = d *  Math.asinh(B / sqrt_d);
        // let zeroLastPart = sqrt_a * 2 * B * Math.sqrt(C);
        //
        // let fz = prePart + lastPart - (zeroPrePart + zeroLastPart);
        // return fz / fm;


        /**
         * ((4*a*c-b^2)*log(2*sqrt(a)*sqrt(a*x^2+b*x+c)+2*a*x+b)+sqrt(a)*(4*a*x+2*b)*sqrt(a*x^2+b*x+c))/(8*a^(3/2))
         */
        let sqrt_a = Math.sqrt(A);
        let sqrt_tmp = Math.sqrt(A * t * t + B * t + C);
        let prePart = d * Math.log(2 * sqrt_a * sqrt_tmp + 2 * A * t + B);
        let lastPart = sqrt_a * (4 * A  * t + 2 * B) * sqrt_tmp;

        let fm = 8 * Math.pow(A,1.5);

        let sqrt_c = Math.sqrt(C);
        let zeroPrePart = d * Math.log(2 * sqrt_a * sqrt_c  + B);
        let zeroLastPart = sqrt_a * 2 * B * sqrt_c;
        let fz = prePart + lastPart - (zeroPrePart + zeroLastPart);
        return fz / fm;
    }

    /**
     *
     * 牛顿迭代 求 f(t) - l  = 0 的近视解 t
     * X_n+1 = X_n - f(X_n) / f'(X_n)
     * @param t
     * @param l
     * @return {number}
     */
    solve(t,l){
        let t1 = t;
        let t2;

        let X_n = 0;
        let df_Xn = 0;
        do{
            X_n = this.ft(t1) - l;

            // f'(X_n)
            df_Xn = this.dft(t1);

            if(df_Xn === 0){
                t2 = t1;
                break;
            }else{
                t2 = t1 - X_n / df_Xn;

                if(Math.abs( t1 - t2) < 0.0001){
                    break;
                }
                t1 = t2;
            }
        }while (true); // eslint-disable-line
        return t2;
    }

    split(){
        if(this.iStep < 1){
            return [];
        }

        let p0 = this.p0;
        let p1 = this.p1;
        let p2 = this.p2;

        let ret = [];
        for(let i = 1; i <= this.iStep; ++i){
            let t = i / this.iStep;
            let l = t * this.totalLength;
            t = this.solve(t,l);

            let xx = ( 1 - t ) * ( 1 - t) * p0.x + 2 * (1 - t) * t * p1.x + t * t * p2.x;
            let yy = ( 1 - t ) * ( 1 - t) * p0.y + 2 * (1 - t) * t * p1.y + t * t * p2.y;

            // get tangent
            let Q0 = cc.v2((1 - t) * p0.x + t * p1.x, (1 - t) * p0.y + t * p1.y);
            let Q1 = cc.v2((1 - t) * p1.x + t * p2.x, (1 - t) * p1.y + t * p2.y);

            // compute degress
            let dx = Q1.x - Q0.x;
            let dy = Q1.y - Q0.y;
            let radians = Math.atan2(dy, -dx);
            let degrees = radians * 180 / Math.PI;

            ret.push([xx,yy,degrees]);
        }
        return ret;
    }
}

module.exports = MyBezier;
